"""
If for debugging reasons you need to visualize relationships between objects you can use this fine tool.

This is extremely useful when trying to figure out why something got stuck in the garbage.
"""

import gc
import inspect

def visualizeObjectMap(targetFile, objects, maxDepth=10, ignoreObjects = []):
    """
    Creates a dot file with the objects relationships.

    .. warning::
        Beware of using infinite depth on objects that are not in the garbage. These objects relate to the all of the objects in the running python instance.

    :param targetFile: The path to where you want to save the resulting dot file.
    :param objects: A list of objects you want to map.
    :param maxDepth: The maximum nodes you want to hop before stopping. -1 is for infinite depth.
    :param ignoreObjects: Objects you don't want to see in the result.
    """
    passedObjects = []
    dotfile = open("/tmp/garbage.dot", "w")
    dotfile.write("digraph G {\n")
    ignoreObjects.extend([objects, gc.garbage])
    for obj in objects:
        objId = id(obj)
        dotfile.write("obj%s [style=filled, color=\".7 .3 1.0\"];\n" % objId)
        _processNode(obj, dotfile, passedObjects, maxDepth, ignoreObjects)
    dotfile.write("}")
    dotfile.close()

def _processNode(obj, dotfile, passedObjects, depth, ignore):
    objId = id(obj)
    passedObjects.append(objId)
    dotfile.write("obj%s [label=\"%s\"];\n" % (objId, obj.__class__.__name__))

    if depth == 0:
        return

    tmplist = list(gc.get_referrers(obj))
    ignore.append(tmplist)
    for i in range(len(tmplist)):
        referrer = tmplist[i]
        referrerId = id(referrer)
        if (referrer in ignore) or (inspect.isframe(referrer)) or referrer is ignore:
            continue

        dotfile.write("obj%s -> obj%s;\n" % (referrerId, objId))

        if referrerId in passedObjects:
            continue

        _processNode(referrer, dotfile, passedObjects, depth - 1, ignore)

